package hu.bme.mit.androtext.gen;

import hu.bme.mit.androtext.androapp.androAppDsl.AndroidApplication;
import hu.bme.mit.androtext.androdata.androDataDsl.AndroDataModelRoot;
import hu.bme.mit.androtext.androgen.androGenDsl.TargetApplication;
import hu.bme.mit.androtext.gen.util.AndroidConstants2;
import hu.bme.mit.androtext.gen.util.AndroidProjectUtil;
import hu.bme.mit.androtext.gen.util.AndroidSdkUtil;

import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.eclipse.core.resources.IContainer;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IFolder;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IProjectDescription;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.resources.IWorkspace;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.OperationCanceledException;
import org.eclipse.core.runtime.Path;
import org.eclipse.core.runtime.SubProgressMonitor;
import org.eclipse.emf.common.util.URI;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.resource.Resource;
import org.eclipse.emf.ecore.resource.ResourceSet;
import org.eclipse.jdt.core.IJavaProject;
import org.eclipse.jdt.core.JavaCore;
import org.eclipse.xtext.builder.EclipseResourceFileSystemAccess;
import org.eclipse.xtext.generator.IFileSystemAccess;

import com.android.AndroidConstants;
import com.android.ide.eclipse.adt.AdtConstants;
import com.android.ide.eclipse.adt.AdtPlugin;
import com.android.ide.eclipse.adt.internal.project.AndroidNature;
import com.android.ide.eclipse.adt.internal.project.ProjectHelper;
import com.android.ide.eclipse.adt.internal.sdk.Sdk;
import com.android.io.StreamException;
import com.android.sdklib.IAndroidTarget;
import com.android.sdklib.SdkConstants;
import com.google.inject.Inject;
import com.google.inject.Provider;

/**
 * Default implementation of the {@link IAndroidApplicationGenerator} interface. Generates Android applications from the *.age files.
 * 
 * @author Mark Czotter
 *
 */
@SuppressWarnings("restriction")
public class AndroidApplicationGenerator implements
		IAndroidApplicationGenerator {
	
	@Inject
	private IEntityClassGenerator entityClassGenerator;
	
	@Inject 
	private IAndroidManifestGenerator manifestGenerator;
	
	@Inject 
	private Provider<EclipseResourceFileSystemAccess> fileSystemAccessProvider;
	
	@Inject 
	private Provider<ResourceSet> resourceSetProvider;

	@Override
	public void generate(IProgressMonitor monitor, IFile modelFile) {
		try {
//			EObject root = getResourceRoot(modelFile);
//			if (!(root instanceof AndroidApplication)) {
//				throw new RuntimeException("The root of the model is not a AndroidApplication!");
//			}
//			// create eclipse project
//			AndroidApplication androidApplication = (AndroidApplication) root;
//			IProject project = createEclipseProject(monitor, androidApplication.getProjectName(), 8, true);
//			
//			// create the packages inside the project
//			// TODO add the other src folder for abstraction
//			IFolder src = project.getFolder(FD_SRC_GEN);
//			Map<String, IFolder> packageNamesToFolders = createPackages(src, getAllPackageNames(androidApplication), monitor);
//			
//			// configure a filesystem access for the generator components
//			IFileSystemAccess fsa = getConfiguredFileSystemAccess(project, androidApplication, packageNamesToFolders);
//			
//			// do the actual code generation
//			doCodeGeneration(androidApplication, project, fsa);
//
//			// refresh the project folder and its contents
//			monitor.subTask("Refreshing the project");
//			project.refreshLocal(IResource.DEPTH_INFINITE,
//					new SubProgressMonitor(monitor, 10));
			// TODO maybe add build call to project after the code generation
		} catch (Exception e) {
			throw new RuntimeException(e.getMessage());
		}
	}
	
	private void doCodeGeneration(TargetApplication targetApplication,
			IProject project, IFileSystemAccess fsa) {
//		this.manifestGenerator.doGenerate(targetApplication.eResource(), fsa, targetApplication);
//		// generate entities if defined
//		AndroidApplication androidApplication = targetApplication.getApplication();
//		AndroDataModelRoot androDataRoot = androidApplication.getDataroot();
//		if (androDataRoot != null) {
//			this.entityClassGenerator.doGenerate(androidApplication.getDataroot().eResource(), fsa, targetApplication);
//		}
	}

	private IFileSystemAccess getConfiguredFileSystemAccess(IProject project, TargetApplication targetApplication, Map<String, IFolder> packageNamesToFolders) {
		EclipseResourceFileSystemAccess fsa = fileSystemAccessProvider.get();
		// set the default output path
		fsa.setOutputPath(packageNamesToFolders.get(targetApplication.getPackageName()).getFullPath().toString());
		// set the src-gen folder path
		fsa.setOutputPath("SRCGEN", project.getFolder(FD_SRC_GEN).getFullPath().toString());
		// set the main project folder path
		fsa.setOutputPath("PROJECT", project.getFullPath().toString());
		// set resource folders
		fsa.setOutputPath(AndroidConstants.FD_RES_DRAWABLE, project.getFolder(AndroidConstants2.RES_DIRECTORY + AndroidConstants2.DRAWABLE_DIRECTORY).getFullPath().toString());
		fsa.setOutputPath(AndroidConstants.FD_RES_LAYOUT, project.getFolder(AndroidConstants2.RES_DIRECTORY + AndroidConstants2.LAYOUT_DIRECTORY).getFullPath().toString());
		fsa.setOutputPath(AndroidConstants.FD_RES_VALUES, project.getFolder(AndroidConstants2.RES_DIRECTORY + AndroidConstants2.VALUES_DIRECTORY).getFullPath().toString());
		fsa.setOutputPath(AndroidConstants.FD_RES_XML, project.getFolder(AndroidConstants2.RES_DIRECTORY + AndroidConstants2.XML_DIRECTORY).getFullPath().toString());
		// set the package folder paths
		for (String packageName : getAllPackageNames(targetApplication)) {
			// skip the main package name, because that folder already set in the beginning of this method
			if (packageName.equals(targetApplication.getPackageName())) {
				continue;
			}
			IFolder folder = packageNamesToFolders.get(packageName);
			if (folder != null) {
				fsa.setOutputPath(getNameForPackageName(packageName), folder.getFullPath().toString());
			} else {
				System.out.println("Found a package '" +packageName+ "' that does not have a IFolder instance in the packageNamesToFolders Map!");
			}
		}
		return fsa;
	}
	
	private List<String> getAllPackageNames(TargetApplication androidApplication) {
		List<String> allPackageNames = new ArrayList<String>();
		allPackageNames.add(androidApplication.getPackageName());
		// data package for entity generation
		allPackageNames.add(androidApplication.getPackageName() + ".data");
		return allPackageNames;
	}
	
	private String getNameForPackageName(String pName) {
		if (pName == null) {
			return null;
		}
		if (pName.contains(".")) {
			int lastDot = pName.lastIndexOf(".");
			return pName.substring(lastDot + 1).toUpperCase();
		} else {
			return pName;
		}
	}

	/**
	 * Loads a resource if not loaded already and returns the root object.
	 * 
	 * @param modelFile
	 * @return the model root
	 */
	public EObject getResourceRoot(IFile modelFile) {
		try {
			URI uri = URI.createPlatformResourceURI(modelFile.getFullPath().toOSString(), true);
			ResourceSet set = resourceSetProvider.get();
			Resource model = set.getResource(uri, true);
			if (model != null) {
				System.out.println("Loaded resource: " + uri);
				return model.getContents().get(0);
			} else {
				throw new RuntimeException(
						"The model is wrong! No contents, or not a correct model file!");
			}
		} catch (Exception e) {
			throw new RuntimeException(e.getMessage());
		}
	}
	
	/**
	 * Creates a folder in the given container.
	 * 
	 * @param container
	 * @param folderName
	 * @param monitor
	 * @throws CoreException
	 */
	private IFolder createFolder(IContainer container, String folderName,
			IProgressMonitor monitor) throws CoreException {
		if (folderName.length() > 0) {
			IFolder folder = container.getFolder(new Path(folderName));
			if (!folder.exists()) {
				folder.create(IResource.BACKGROUND_REFRESH, true, new SubProgressMonitor(monitor, 10));
			}
			return folder;
		}
		return null;
	}
	
	/**
	 * Creates packages in the given src folder.
	 * 
	 * @param src
	 * @param allPackageNames
	 * @param monitor
	 * @return a Map containing all the created IFolder instance mapped with their packageName.
	 * @throws CoreException
	 */
	private Map<String, IFolder> createPackages(IFolder src,
			List<String> allPackageNames, IProgressMonitor monitor)
			throws CoreException {
		Map<String, IFolder> packages = new HashMap<String, IFolder>();
		for (String packageName : allPackageNames) {
			if (packageName == null || packageName.isEmpty()) {
				continue;
			}
			if (packageName.contains(".")) {
				String[] packageFragments = packageName.split("[//.]");
				IFolder fragmentContainer = src;
				for (String fragment : packageFragments) {
					fragmentContainer = createFolder(fragmentContainer,
							fragment, new SubProgressMonitor(monitor, 5));
				}
				if (fragmentContainer != null) {
					packages.put(packageName, fragmentContainer);
				}
			} else {
				IFolder folder = createFolder(src, packageName,
						new SubProgressMonitor(monitor, 5));
				if (folder != null) {
					packages.put(packageName, folder);
				}
			}
		}
		return packages;
	}
	
}
